
#ifndef _WIN32_WINNT            
#define _WIN32_WINNT 0x0500     // Change this to the appropriate value to target other versions of Windows.
#endif

#include <stdio.h>
#include <tchar.h>
#include "synchrony.h"

#define _RAND_BASE 0
#define _MAX_CYCLES 1000

// ...

void thread_A()
{
	printf("Go Synapse(Write) Thread A...\n");
	srand(_RAND_BASE+1);
	Synapse<int> myValues;
	myValues.Open("Shared_Test");
	for (size_t i = 0; i < _MAX_CYCLES; i++)
	{
		Sleep(rand()%2);
		//myValues.WaitForRead();
		if (myValues.IsOld())
		{
			myValues.Write(1);
		}
		if (myValues.ReadCopy(1) == 2)
		{
			break;
		}
	}
	printf("Thread A: Finished\n");
}

void thread_B()
{
	printf("Go Synapse(Write) Thread B...\n");
	srand(_RAND_BASE+2);
	Synapse<int> myValues;
	myValues.Open("Shared_Test");
	for (size_t i = 0; i < _MAX_CYCLES; i++)
	{
		Sleep(rand()%3);
		//myValues.WaitForRead();
		if (myValues.IsOld())
		{
			myValues.Write(2);
		}
		if (myValues.ReadCopy(1) == 2)
		{
			break;
		}
	}
	printf("Thread B: Finished\n");
}

void thread_C()
{
	printf("Go Synapse(Read) Thread C...\n");
	srand(_RAND_BASE+3);
	Synapse<int> myValues;
	myValues.Open("Shared_Test");
	int k[_MAX_CYCLES];
	size_t i = 0;
	size_t c = 0;
	while (i < _MAX_CYCLES)
	{
		//Sleep(rand()%10);
		k[i] = myValues.ReadCopyWhenNew();
		if (k[i++] == 1) c++;
	}
	myValues.WriteLock();
	myValues.Grab(1)++;
	myValues.SetAsRead();
	myValues.Unlock();
	size_t s = 0;
	for (i = 0; i < _MAX_CYCLES; i++)
	{
		s += k[i];
	}
	printf("Thread C: Read %d (of %d) values of 1 :: sum total of %d\n", c, i, s);
}

void thread_D()
{
	printf("Go Synapse(Read) Thread D...\n");
	srand(_RAND_BASE+4);
	Synapse<int> myValues;
	myValues.Open("Shared_Test");
	int k[_MAX_CYCLES];
	size_t i = 0;
	size_t c = 0;
	while (i < _MAX_CYCLES)
	{
		//Sleep(rand()%25);
		k[i] = myValues.ReadCopyWhenNew();
		if (k[i++] == 2) c++;
	}
	myValues.WriteLock();
	myValues.Grab(1)++;
	myValues.SetAsRead();
	myValues.Unlock();
	size_t s = 0;
	for (i = 0; i < _MAX_CYCLES; i++)
	{
		s += k[i];
	}
	printf("Thread D: Read %d (of %d) values of 2 :: sum total of %d\n", c, i, s);
}

// ...

void synchrony_A()
{
	printf("Go Synchrony(Write) Thread A...\n");
	srand(_RAND_BASE+1);
	Synchrony<int> myBuffer;
	myBuffer.Open("Synchrony_Test");
	int n = 0;
	for (size_t i = 0; i < _MAX_CYCLES; i++, n++)
	{
		SynchroSample<int> sample(&n);
		myBuffer.Push(sample);
		Sleep(rand()%2);
	}
	printf("Thread A: Finished\n");
}

void synchrony_B()
{
	printf("Go Synchrony(Write) Thread B...\n");
	srand(_RAND_BASE+2);
	Synchrony<int> myBuffer;
	myBuffer.Open("Synchrony_Test");
	int n = _MAX_CYCLES;
	for (size_t i = 0; i < _MAX_CYCLES; i++, n++)
	{
		SynchroSample<int> sample(&n);
		myBuffer.Push(sample);
		Sleep(rand()%2);
	}
	printf("Thread B: Finished\n");
}

void synchrony_C()
{
	printf("Go Synchrony(Read) Thread C...\n");
	srand(_RAND_BASE+3);
	Synchrony<int> myBuffer;
	myBuffer.Open("Synchrony_Test");
	int k[_MAX_CYCLES];	
	size_t i = 0;
	size_t c = 0;
	int lastVal = 0;
	while (i < _MAX_CYCLES/2)
	{
		//Sleep(rand()%2);
		SynchroSample<int> sample = myBuffer.WaitReadNext();
		if (sample.GetData() < _MAX_CYCLES) 
		{
			//if (i == 400) myBuffer.Reset();
			if (sample.GetData() < lastVal)
			{
				printf("Thread C: %d (item %d) should not be less than %d\n", sample.GetData(), i, k[c-1]);
			}
			lastVal = k[c++] = sample.GetData();
		}
		i++;
	}
	size_t s = 0;
	for (size_t j = 0; j < c; j++)
	{
		//printf("%d ", k[j]);
		s += k[j];
	}
	printf("Thread C: Read %d (of %d) values of <1000 :: average value is %f\n", c, i, s/(float)c);
}

void synchrony_D()
{
	printf("Go Synchrony(Read) Thread D...\n");
	srand(_RAND_BASE+3);
	Synchrony<int> myBuffer;
	myBuffer.Open("Synchrony_Test");

	int k[_MAX_CYCLES];	
	size_t i = 0;
	size_t c = 0;
	int lastVal = 0;
	while (i < _MAX_CYCLES/2)
	{
		//Sleep(rand()%2);
		SynchroSample<int> sample = myBuffer.WaitReadNext();
		if (sample.GetData() >= _MAX_CYCLES) 
		{
			if (sample.GetData() < lastVal)
			{
				printf("Thread D: %d (item %d) should not be less than %d\n", sample.GetData(), i, k[c-1]);
			}
			lastVal = k[c++] = sample.GetData();
		}
		i++;
	}
	size_t s = 0;
	for (size_t j = 0; j < c; j++)
	{
		//printf("%d ", k[j]);
		s += k[j];
	}
	printf("Thread D: Read %d (of %d) values of >=1000 :: average value is %f\n", c, i, s/(float)c);
}

void synchrony_T()
{
	Synchrony<int> myBuffer;
	myBuffer.SetBufferSizeInUnits(100);
	myBuffer.SetInterval(24);
	myBuffer.SetTimeSlots(5);
	myBuffer.Create("Synchrony_SyncTest");

	// test
	/*myBuffer.ReadNext();
	myBuffer.ReadNewest();
	myBuffer.ReadOldest();
	myBuffer.WaitReadAtTime(123);
	myBuffer.ReadAtSlot(7);
	myBuffer.CountAtTime(999);
	myBuffer.CountAtSlot(0);
	vector<SynchroSample<int>> ia;
	myBuffer.ReadAllAtSlot(ia, 1);
	myBuffer.ReadAllAtTime(ia, 111111);
	Synchrony<int>::Exists("hello");
	myBuffer.GetName();
	myBuffer.IsOpen();
	myBuffer.GetInfo();*/
	// ----
	
	myBuffer.PushTime(4);

	int n = 0;
	for (size_t i = 0; i < 100; i++, n++)
	{
		SynchroSample<int> sample(false, &n, 1, 5+n);
		myBuffer.Push(sample);
	}

	printf("Count:");
	vector<long> countArray;
	vector<long long> timeArray;
	myBuffer.ListSlotCounts(countArray);
	for (size_t i = 0; i < countArray.size(); i++)
	{
		printf(" %ld", countArray[i]);
	}
	printf("\nTimes:");
	myBuffer.ListSlotTimes(timeArray);
	for (size_t i = 0; i < timeArray.size(); i++)
	{
		printf(" %lld", timeArray[i]);
	}
	printf("\nSamples retrieved:");
	for (size_t i = 0; i < 105; i++)
	{
		SynchroSample<int> sample; 
		if (myBuffer.WaitReadAtTime(sample, i, 0))
		//if (myBuffer.ReadFirstAtTimeOrPrevious(sample, i))
		{
			long long stime = sample.GetStartTime();
			printf(" %d:%d(%lld)", i, sample.GetData(), stime);
		}
		else
		{
			printf(" *");
		}
	}
	printf(" ... Finished\n");

}

// ...

int _tmain(int argc, _TCHAR* argv[])
{
	HANDLE threadH[4];

	/*printf("!! Starting Synapse test...\n");

	Synapse<int> myValues;
	myValues.Create("Shared_Test", 2);

	threadH[0] = CreateThread( 
					NULL,       // default security attributes
					0,          // default stack size
					(LPTHREAD_START_ROUTINE) thread_A, 
					NULL,		// thread function argument
					0,          // default creation flags
					NULL	    // receive thread identifier
				);

	threadH[1] = CreateThread( 
					NULL,       // default security attributes
					0,          // default stack size
					(LPTHREAD_START_ROUTINE) thread_B, 
					NULL,		// thread function argument
					0,          // default creation flags
					NULL	    // receive thread identifier
				);
	threadH[2] = CreateThread( 
					NULL,       // default security attributes
					0,          // default stack size
					(LPTHREAD_START_ROUTINE) thread_C, 
					NULL,		// thread function argument
					0,          // default creation flags
					NULL	    // receive thread identifier
				);

	threadH[3] = CreateThread( 
					NULL,       // default security attributes
					0,          // default stack size
					(LPTHREAD_START_ROUTINE) thread_D, 
					NULL,		// thread function argument
					0,          // default creation flags
					NULL	    // receive thread identifier
				);

	WaitForMultipleObjects( 
        4,           // number of objects in array
        threadH,     // array of objects
        TRUE,       // wait for any object
        INFINITE);      */

	printf("!! Starting Synchrony test...\n");

	Synchrony<int> myBuffer;
	myBuffer.SetBufferSizeInUnits(1000);
	myBuffer.SetInterval();
	myBuffer.SetTimeSlots();
	myBuffer.Create("Synchrony_Test");

	threadH[0] = CreateThread( 
					NULL,       // default security attributes
					0,          // default stack size
					(LPTHREAD_START_ROUTINE) synchrony_A, 
					NULL,		// thread function argument
					0,          // default creation flags
					NULL	    // receive thread identifier
				);

	threadH[1] = CreateThread( 
					NULL,       // default security attributes
					0,          // default stack size
					(LPTHREAD_START_ROUTINE) synchrony_C, 
					NULL,		// thread function argument
					0,          // default creation flags
					NULL	    // receive thread identifier
				);

	threadH[2] = CreateThread( 
					NULL,       // default security attributes
					0,          // default stack size
					(LPTHREAD_START_ROUTINE) synchrony_B, 
					NULL,		// thread function argument
					0,          // default creation flags
					NULL	    // receive thread identifier
				);

	threadH[3] = CreateThread( 
					NULL,       // default security attributes
					0,          // default stack size
					(LPTHREAD_START_ROUTINE) synchrony_D, 
					NULL,		// thread function argument
					0,          // default creation flags
					NULL	    // receive thread identifier
				);

	WaitForMultipleObjects( 
        4,           // number of objects in array
        threadH,     // array of objects
        TRUE,       // wait for any object
		INFINITE);      


	threadH[0] = CreateThread( 
					NULL,       // default security attributes
					0,          // default stack size
					(LPTHREAD_START_ROUTINE) synchrony_T, 
					NULL,		// thread function argument
					0,          // default creation flags
					NULL	    // receive thread identifier
				);
	WaitForMultipleObjects( 
        1,           // number of objects in array
        threadH,     // array of objects
        TRUE,       // wait for any object
		INFINITE);      

	printf("!! Done.\n");

	return 0;
}

